package com.glodon.paas.account.security.cas.ticket.registry;

import com.glodon.paas.account.security.cas.ticket.support.RedisExpirationPolicy;
import com.google.common.collect.Lists;
import org.jasig.cas.monitor.TicketRegistryState;
import org.jasig.cas.ticket.ServiceTicketImpl;
import org.jasig.cas.ticket.Ticket;
import org.jasig.cas.ticket.TicketGrantingTicketImpl;
import org.jasig.cas.ticket.registry.AbstractTicketRegistry;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;

import java.util.Collection;
import java.util.List;

/**
 * Redis implementation of a CAS {@link org.jasig.cas.ticket.registry.TicketRegistry}.
 *
 * @author Don Li
 */
public final class RedisTicketRegistry extends AbstractTicketRegistry implements TicketRegistryState {

    public static final String DEFAULT_TGT_PREFIX = "CAS:TGT:";
    public static final String DEFAULT_ST_PREFIX = "CAS:ST:";

    private final String keyPrefix;
    private final RedisTemplate<String, Ticket> template;
    private final ValueOperations<String, Ticket> operations;
    private final RedisExpirationPolicy tgtExpirationPolicy;
    private final RedisExpirationPolicy stExpirationPolicy;
    private JedisPool pool;

    public RedisTicketRegistry(String keyPrefix, RedisTemplate<String, Ticket> template,
                               RedisExpirationPolicy tgtExpirationPolicy, RedisExpirationPolicy stExpirationPolicy) {
        this.keyPrefix = keyPrefix;
        this.template = template;
        this.tgtExpirationPolicy = tgtExpirationPolicy;
        this.stExpirationPolicy = stExpirationPolicy;
        this.operations = template.opsForValue();
    }

    @Override
    public void addTicket(Ticket ticket) { // Spring Data Redis is not ready for transaction now.
        final Jedis jedis = pool.getResource();
        Transaction transaction = jedis.multi();
        final String key = keyPrefix + ticket.getId();
        transaction.set(key.getBytes(), new JdkSerializationRedisSerializer().serialize(ticket));
        if (ticket instanceof TicketGrantingTicketImpl) {
            transaction.expire(key, tgtExpirationPolicy.getTtl());
        } else if (ticket instanceof ServiceTicketImpl) {
            transaction.expire(key, stExpirationPolicy.getTtl());
        }
        transaction.exec();
        pool.returnResource(jedis);
    }


    @Override
    public Ticket getTicket(String ticketId) {
        return operations.get(keyPrefix + ticketId);
    }

    @Override
    public boolean deleteTicket(String ticketId) {
        template.delete(keyPrefix + ticketId);
        return true;
    }

    @Override
    public Collection<Ticket> getTickets() {
        List<Ticket> tickets = Lists.newArrayList();
        for (String key : template.keys(keyPrefix + "*")) {
            tickets.add(operations.get(key));
        }
        return tickets;
    }

    @Override
    public int sessionCount() {
        return 0;  //TODO externalize all the sso sessions
    }

    @Override
    public int serviceTicketCount() {
        return 0;  //TODO mantain the relationship betweem tgt and st
    }

    public void setPool(JedisPool pool) {
        this.pool = pool;
    }
}
